package com.zbkj.client.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.anji.captcha.model.common.ResponseModel;
import com.zbkj.client.config.ClientTokenComponent;
import com.zbkj.client.service.MaterialService;
import com.zbkj.common.constants.Constants;
import com.zbkj.common.constants.RedisConstants;
import com.zbkj.common.constants.SmsConstants;
import com.zbkj.common.constants.SysConfigConstants;
import com.zbkj.common.enums.RoleEnum;
import com.zbkj.common.exception.CrmebException;
import com.zbkj.common.model.admin.SystemAdmin;
import com.zbkj.common.model.assets.Material;
import com.zbkj.common.model.client.agent.PowerPointHistory;
import com.zbkj.common.request.AjAdminLoginRequest;
import com.zbkj.common.request.ClientLoginRequest;
import com.zbkj.common.request.ClientSendCodeRequest;
import com.zbkj.common.request.ClientUpdatePasswordRequest;
import com.zbkj.common.response.ClientLoginResponse;
import com.zbkj.common.response.SystemLoginResponse;
import com.zbkj.common.result.CommonResultCode;
import com.zbkj.common.token.FrontTokenComponent;
import com.zbkj.common.utils.CrmebUtil;
import com.zbkj.common.utils.OssUtil;
import com.zbkj.common.utils.RedisUtil;
import com.zbkj.client.service.ClientLoginService;
import com.zbkj.common.vo.LoginUserVo;
import com.zbkj.service.service.PowerPointHistoryService;
import com.zbkj.service.service.SmsService;
import com.zbkj.service.service.SystemAdminService;
import com.zbkj.client.utils.SmsUtil;
import com.zbkj.service.service.SystemConfigService;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 客户端登录服务实现类
 * +----------------------------------------------------------------------
 * | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2016~2025 https://www.crmeb.com All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
 * +----------------------------------------------------------------------
 * | Author: CRMEB Team <admin@crmeb.com>
 * +----------------------------------------------------------------------
 */
@Slf4j
@Service
public class ClientLoginServiceImpl implements ClientLoginService {

    @Resource
    private SmsService smsService;

    @Resource
    private SystemAdminService systemAdminService;

    @Resource
    private FrontTokenComponent frontTokenComponent;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private SystemConfigService systemConfigService;

    @Resource
    private ClientTokenComponent clientTokenComponent;

    @Resource
    private PowerPointHistoryService powerPointHistoryService;

    @Resource
    private MaterialService materialService;

    @Resource
    private SmsUtil smsUtil;

    /**
     * 发送验证码
     *
     * @param request 发送验证码请求
     * @return Boolean
     */
    @Override
    public Boolean sendCode(ClientSendCodeRequest request) {
        // 1. 生成一个 100000-999999的6位的随机数
        int randomNum = (int) (Math.random() * 900000) + 100000;
        
        // 2. 获取手机号
        String phoneRequest = request.getPhone();
        
        // 3. 查询表eb_system_admin表中，查找phone=phoneRequest, 且is_del=0, 且type in (2,4)
        SystemAdmin systemAdmin = systemAdminService.lambdaQuery()
                .eq(SystemAdmin::getPhone, phoneRequest)
                .eq(SystemAdmin::getIsDel, 0)
                .in(SystemAdmin::getType, Arrays.asList(2, 4))
                .one();
        
        if (systemAdmin == null) {
            throw new CrmebException("用户不存在");
        }
        
        Integer userId = systemAdmin.getId();
        
        // 4. 发送短信验证码
        Boolean sendRes = smsUtil.sendCode(phoneRequest, String.valueOf(randomNum));
        
        // 5. 如果发送成功，将验证码存入redis中，过期时间5分钟
        if (sendRes) {
            String redisKey = "C:L:" + phoneRequest;
            redisUtil.set(redisKey, String.valueOf(randomNum), 5L, TimeUnit.MINUTES);
            log.info("验证码发送成功，手机号: {}, 验证码: {}, Redis Key: {}", phoneRequest, randomNum, redisKey);
        } else {
            log.error("验证码发送失败，手机号: {}", phoneRequest);
        }
        
        return sendRes;
    }

    /**
     * 客户端登录
     *
     * @param request 登录请求
     * @return ClientLoginResponse
     */
    @Override
    public ClientLoginResponse login(ClientLoginRequest request, String ip) {
        SystemAdmin systemAdmin = null;

        if (request.getType() == 1) {
            // 用户名密码登录
            if (StrUtil.isBlank(request.getUsername()) || StrUtil.isBlank(request.getPassword())) {
                throw new CrmebException("用户名和密码不能为空");
            }
            systemAdmin = loginByUsernameAndPassword(request.getUsername(), request.getPassword());
        } else if (request.getType() == 2) {
            // 手机号验证码登录
            if (StrUtil.isBlank(request.getPhone()) || StrUtil.isBlank(request.getCode())) {
                throw new CrmebException("手机号和验证码不能为空");
            }
            systemAdmin = loginByPhoneAndCode(request.getPhone(), request.getCode());
        } else {
            throw new CrmebException("不支持的登录类型");
        }

        if (systemAdmin == null) {
            throw new CrmebException("登录失败，用户不存在或信息错误");
        }

        // 检查用户状态
        if (!systemAdmin.getStatus()) {
            throw new CrmebException("用户已被禁用");
        }

        // 更新登录信息
        updateLoginInfo(systemAdmin);

        // 生成token
        String token = generateToken(systemAdmin);

        // 构建响应
//        ClientLoginResponse response = new ClientLoginResponse();
//        response.setToken(token);
//        response.setUsername(systemAdmin.getAccount());
//        response.setPhone(systemAdmin.getPhone());
//        response.setMerId(systemAdmin.getMerId());
//        response.setRealName(systemAdmin.getRealName());
//        response.setId(systemAdmin.getId());

        ClientLoginResponse response = clientTokenComponent.getUserInfo(systemAdmin.getId(), token);
        response.setToken(token);

        return response;
    }

    /**
     * 验证手机验证码是否正确
     *
     * @param phone 手机号
     * @param code 验证码
     * @param type 验证码类型
     * @return Boolean
     */
    private Boolean checkValidateCode(String phone, String code, String type) {
        String redisKey;
        if ("login".equals(type)) {
            redisKey = "C:L:" + phone;
        } else {
            throw new CrmebException("不支持的验证码类型");
        }
        
        String storedCode = redisUtil.get(redisKey);
        if (storedCode == null) {
            log.warn("验证码已过期或不存在，手机号: {}, 类型: {}", phone, type);
            return false;
        }
        
        boolean isValid = storedCode.equals(code);
        if (isValid) {
            // 验证成功后删除Redis中的验证码
            redisUtil.delete(redisKey);
            log.info("验证码验证成功，手机号: {}, 类型: {}", phone, type);
        } else {
            log.warn("验证码验证失败，手机号: {}, 输入验证码: {}, 存储验证码: {}, 类型: {}", 
                    phone, code, storedCode, type);
        }
        
        return isValid;
    }

    /**
     * 用户名密码登录
     *
     * @param username 用户名
     * @param password 密码
     * @return SystemAdmin
     */
    private SystemAdmin loginByUsernameAndPassword(String username, String password) {
        // 参数验证
        if (StrUtil.isBlank(username)) {
            throw new CrmebException("用户名不能为空");
        }
        if (StrUtil.isBlank(password)) {
            throw new CrmebException("密码不能为空");
        }
        
        // 用户验证
        Authentication authentication = null;
        // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
        try {
            // 客户端登录只允许商户管理员类型(type=4)的用户登录
            String principal = username + RoleEnum.MERCHANT_ADMIN.getValue();
            log.info("客户端登录，用户名: {}, principal: {}", username, principal);
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(principal, password));
        } catch (AuthenticationException e) {
            log.error("客户端登录认证失败，用户名: {}, 错误: {}", username, e.getMessage());
            if (e instanceof BadCredentialsException) {
                throw new CrmebException("用户不存在或密码错误");
            }
            throw new CrmebException(e.getMessage());
        } catch (CrmebException e) {
            log.error("客户端登录业务异常，用户名: {}, 错误: {}", username, e.getMessage());
            throw new CrmebException("账号或者密码不正确");
        } catch (Exception e) {
            log.error("客户端登录系统异常，用户名: {}, 错误: {}", username, e.getMessage(), e);
            // 返回更详细的错误信息，便于调试
            throw new CrmebException("登录失败: " + e.getMessage());
        }
        
        if (authentication == null || authentication.getPrincipal() == null) {
            throw new CrmebException("登录失败，无法获取用户信息");
        }
        
        LoginUserVo loginUser = (LoginUserVo) authentication.getPrincipal();
        SystemAdmin systemAdmin = loginUser.getUser();
        
        // 额外验证：确保用户确实是商户管理员类型
        if (!systemAdmin.getType().equals(RoleEnum.MERCHANT_ADMIN.getValue()) && 
            !systemAdmin.getType().equals(RoleEnum.SUPER_MERCHANT.getValue())) {
            log.error("客户端登录失败，用户类型不允许: 用户名: {}, 用户类型: {}", username, systemAdmin.getType());
            throw new CrmebException("该账号不允许在客户端登录");
        }
        
        return systemAdmin;
    }

    /**
     * 手机号验证码登录
     *
     * @param phone 手机号
     * @param code 验证码
     * @return SystemAdmin
     */
    private SystemAdmin loginByPhoneAndCode(String phone, String code) {
        // 验证验证码
        Boolean isValid = checkValidateCode(phone, code, "login");
        if (!isValid) {
            throw new CrmebException("验证码错误或已过期");
        }

        // 根据手机号查找用户
        SystemAdmin systemAdmin = systemAdminService.selectUserByPhone(phone);
        if (systemAdmin == null) {
            throw new CrmebException("该手机号未注册用户");
        }

        return systemAdmin;
    }

    /**
     * 更新登录信息
     *
     * @param systemAdmin 用户信息
     */
    private void updateLoginInfo(SystemAdmin systemAdmin) {
        // 更新登录次数和最后登录时间
        systemAdmin.setLoginCount(systemAdmin.getLoginCount() + 1);
        systemAdmin.setUpdateTime(new Date());
        systemAdminService.updateById(systemAdmin);
    }

    /**
     * 生成token
     *
     * @param systemAdmin 用户信息
     * @return String
     */
    private String generateToken(SystemAdmin systemAdmin) {
        // 为用户创建登录token
//        String token = UUID.randomUUID().toString().replace("-", "");
        Integer id = systemAdmin.getId();
        String raw = id + "_" + System.currentTimeMillis();
        String token = cn.hutool.crypto.digest.DigestUtil.md5Hex(raw);
        String userTokenKey = Constants.CLIENT_TOKEN_KEY + token;

        // 将用户ID存储到Redis中
        redisUtil.set(userTokenKey, id, Constants.TOKEN_EXPRESS_MINUTES, TimeUnit.MINUTES);
        
        // 返回前端使用的token格式
        return token;
    }

    /**
     * 修改密码
     *
     * @param request 修改密码请求
     * @param userId 当前登录用户ID
     * @return Boolean
     */
    @Override
    public Boolean updatePassword(ClientUpdatePasswordRequest request, Integer userId) {
        // 参数验证
        if (ObjectUtil.isNull(userId)) {
            throw new CrmebException("用户ID不能为空");
        }
        
        if (StrUtil.isBlank(request.getNewPassword())) {
            throw new CrmebException("新密码不能为空");
        }

        // 获取用户信息
        SystemAdmin systemAdmin = systemAdminService.getById(userId);
        if (systemAdmin == null) {
            throw new CrmebException("用户不存在");
        }
        
        // 验证用户状态
        if (!systemAdmin.getStatus()) {
            throw new CrmebException("用户已被禁用");
        }
        
        // 更新密码
        systemAdmin.setPwd(CrmebUtil.encryptPassword(request.getNewPassword(), systemAdmin.getAccount()));
        systemAdmin.setUpdateTime(new Date());
        
        boolean result = systemAdminService.updateById(systemAdmin);
        if (result) {
            log.info("客户端用户修改密码成功，用户ID: {}, 用户名: {}", userId, systemAdmin.getAccount());
        } else {
            log.error("客户端用户修改密码失败，用户ID: {}", userId);
            throw new CrmebException("修改密码失败");
        }
        
        return result;
    }

    /**
     * 更新商户点数
     * 1. 插入表eb_client_power_point_history记录扣点历史
     * 2. 更新租户表的点数
     * 
     * @param userId 用户ID
     * @param merId 租户ID
     * @param module 模块名称
     * @param moduleId 模块ID
     * @param moduleName 模块名称
     * @param moduleName 要显示的名称（如标题等）
     * @param costPoint 花费的点数
     * @return 更新后的剩余点数
     */
    @Override
    public Double updateMerPoint(Integer userId, Integer merId, String module, Integer moduleId, String moduleName,  String showName, Double costPoint,  Integer duration) {
        // 参数验证
        if (userId == null) {
            throw new CrmebException("用户ID不能为空");
        }
        if (merId == null) {
            throw new CrmebException("租户ID不能为空");
        }
        if (StrUtil.isBlank(module)) {
            throw new CrmebException("模块名称不能为空");
        }
        if (costPoint == null || costPoint <= 0) {
            throw new CrmebException("花费点数必须大于0");
        }

        try {
            // 1. 插入扣点历史记录
            PowerPointHistory history = new PowerPointHistory();
            history.setUserId(userId);
            history.setMerId(merId);
            history.setModule(module);
            history.setModuleId(moduleId);
            history.setModuleName(moduleName);
            history.setShowName(showName);
            history.setDuration(duration);
            history.setCostPoint(new BigDecimal(costPoint));
            history.setCreateTime(new Date());
            history.setUpdateTime(new Date());
            
            boolean historyResult = powerPointHistoryService.save(history);
            if (!historyResult) {
                throw new CrmebException("保存扣点历史记录失败");
            }

            // 2. 更新租户表的点数
            Double remainingPoints = clientTokenComponent.updateMerchantPowerPoint(merId, costPoint);
            
            log.info("商户点数更新成功 - 用户ID: {}, 租户ID: {}, 模块: {}, 扣点数: {}, 剩余点数: {}", 
                    userId, merId, module, costPoint, remainingPoints);
            
            return remainingPoints;
            
        } catch (Exception e) {
            log.error("商户点数更新失败 - 用户ID: {}, 租户ID: {}, 模块: {}, 扣点数: {}, 错误: {}", 
                    userId, merId, module, costPoint, e.getMessage(), e);
            throw new CrmebException("更新商户点数失败: " + e.getMessage());
        }
    }

    /**
     * 更新用户头像
     * @param materialId
     * @param userId
     * @param merId
     * @return
     */
    @Override
    public Boolean updateHeadImg(Integer materialId,  Integer userId, Integer merId) {
        Material material = materialService.getById(materialId);
        if (material == null || material.getIsDel() != null && material.getIsDel() == 1) {
            throw new CrmebException("素材不存在或已被删除");
        }
        String url = material.getUrl();
        if (url == null || url.isEmpty()) {
            throw new CrmebException("素材URL为空");
        }
        // 2. 拷贝到新OSS位置
        String fileName = url.substring(url.lastIndexOf('/') + 1);
        String ossUrl = OssUtil.copyOssFile(url, "noasset", merId, userId, "headimg", fileName);
        if (ossUrl == null || ossUrl.isEmpty()) {
            throw new CrmebException("头像更新失败");
        }

        SystemAdmin systemAdmin = systemAdminService.getById(userId);
        systemAdmin.setHeaderImage(ossUrl);
        systemAdmin.setUpdateTime(new Date());

        //更新redis中的headImg字段
        clientTokenComponent.updateUserHeadImg(userId, ossUrl);

        boolean result = systemAdminService.updateById(systemAdmin);

        return result;
    }

} 